import 'dart:convert';
import 'dart:io';
import 'dart:isolate';

import 'package:clipboard/clipboard.dart';
//import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
import 'package:move_to_background/move_to_background.dart';
import 'package:novel_reader/animation_text.dart';
import 'package:novel_reader/apk.dart';
import 'package:novel_reader/files.dart';
import 'package:novel_reader/net_access.dart';
import 'package:novel_reader/taskhandler.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
import 'package:flutter_file_dialog/flutter_file_dialog.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Novel Audio Reader',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // TRY THIS: Try running your application with "flutter run". You'll see
        // the application has a blue toolbar. Then, without quitting the app,
        // try changing the seedColor in the colorScheme below to Colors.green
        // and then invoke "hot reload" (save your changes or press the "hot
        // reload" button in a Flutter-supported IDE, or press "r" if you used
        // the command line to start the app).
        //
        // Notice that the counter didn't reset back to zero; the application
        // state is not lost during the reload. To reset the state, use hot
        // restart instead.
        //
        // This works for code too, not just values: Most code changes can be
        // tested with just a hot reload.
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Read novel with TTS.'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.

  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked "final".

  final String title;

  @override
  State<MyHomePage> createState() => StMyHomePageState();
}

class StMyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
  TextEditingController contentCtrl = TextEditingController();
  TextEditingController urlCtrl = TextEditingController();
  String engine = '';
  bool ttsPause = false;
  int ttsStart = -1;
  static List<String> spkList = List.empty();
  late SharedPreferences _store;
  double speed = 1.0;
  List<dynamic> links = List<List<String>>.empty();
  bool bgRun = false;
  //String errMsg = "状态栏";

  Future<void> _requestPermissionForAndroid() async {
    if (!Platform.isAndroid) {
      return;
    }

    // "android.permission.SYSTEM_ALERT_WINDOW" permission must be granted for
    // onNotificationPressed function to be called.
    //
    // When the notification is pressed while permission is denied,
    // the onNotificationPressed function is not called and the app opens.
    //
    // If you do not use the onNotificationPressed or launchApp function,
    // you do not need to write this code.
    if (!await FlutterForegroundTask.canDrawOverlays) {
      // This function requires `android.permission.SYSTEM_ALERT_WINDOW` permission.
      await FlutterForegroundTask.openSystemAlertWindowSettings();
    }

    // Android 12 or higher, there are restrictions on starting a foreground service.
    //
    // To restart the service on device reboot or unexpected problem, you need to allow below permission.
    if (!await FlutterForegroundTask.isIgnoringBatteryOptimizations) {
      // This function requires `android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS` permission.
      await FlutterForegroundTask.requestIgnoreBatteryOptimization();
    }

    // Android 13 and higher, you need to allow notification permission to expose foreground service notification.
    final NotificationPermission notificationPermissionStatus =
        await FlutterForegroundTask.checkNotificationPermission();
    if (notificationPermissionStatus != NotificationPermission.granted) {
      await FlutterForegroundTask.requestNotificationPermission();
    }
  }

  void _initForegroundTask() {
    FlutterForegroundTask.init(
      androidNotificationOptions: AndroidNotificationOptions(
        channelId: 'novel_reader_service',
        channelName: 'Novel reader notification',
        playSound: true,
        channelImportance: NotificationChannelImportance.DEFAULT,
      ),
      iosNotificationOptions: const IOSNotificationOptions(
        showNotification: true,
        playSound: true,
      ),
      foregroundTaskOptions: const ForegroundTaskOptions(
        interval: 5000,
        isOnceEvent: false,
        autoRunOnBoot: false,
        allowWakeLock: true,
        allowWifiLock: true,
      ),
    );
    FlutterForegroundTask.wakeUpScreen();
  }

  ReceivePort? _receivePort;
  Future<bool> _startForegroundTask() async {
    // You can save data using the saveData function.
    //await FlutterForegroundTask.saveData(key: 'customData', value: 'hello');

    // Register the receivePort before starting the service.
    final ReceivePort? receivePort = FlutterForegroundTask.receivePort;
    final bool isRegistered = _registerReceivePort(receivePort);
    if (!isRegistered) {
      //print('fhz: Failed to register receivePort!');
      return false;
    }

    if (await FlutterForegroundTask.isRunningService) {
      return FlutterForegroundTask.restartService();
    } else {
      return FlutterForegroundTask.startService(
        notificationTitle: 'NovelReader',
        notificationText: 'Playing',
        callback: startCallback,
      );
    }
  }

  bool _registerReceivePort(ReceivePort? newReceivePort) {
    if (newReceivePort == null) {
      return false;
    }

    _closeReceivePort();

    _receivePort = newReceivePort;
    _receivePort?.listen((data) {
      //print('fhz: receivePort: $data');
      if (data is int) {
        int i = data;
        ttsStart = i;
        if (mounted) {
          _store.setInt('ttsStart', i);
          const n = 1;
          if (i < n) {
            contentCtrl.text =
                spkList.sublist(0, n).join('\n==============>\n');
          } else {
            contentCtrl.text =
                spkList.sublist(i - n, i + 1).join('\n==============>\n');
          }
          if (mounted) {
            setState(() {});
          }
        }
      }
    });

    return _receivePort != null;
  }

  void _closeReceivePort() {
    _receivePort?.close();
    _receivePort = null;
  }

  Future<void> startPlay({keep = false}) async {
    if (!keep) ttsStart = 0;
    await _store.setInt('ttsStart', ttsStart);
    //print('fhz: push data ${spkList.length}');
    WakelockPlus.enable();
    ttsPause = false;
    _startForegroundTask();
  }

  void continuePlay() {
    if (ttsStart < 0) return;
    WakelockPlus.enable();
    ttsPause = false;
    _store.setInt('ttsStart', ttsStart).then((value) {
      _startForegroundTask();
    });
  }

  void showText(int i) {
    if (!ttsPause) {
      ttsPause = true;
      FlutterForegroundTask.stopService();
    }
    ttsStart = i;
    if (mounted) {
      //_store.setInt('ttsStart', i);
      if (i == 0) {
        contentCtrl.text = spkList[0];
      } else {
        contentCtrl.text =
            spkList.sublist(i - 1, i + 1).join('\n==============>\n');
      }
      if (mounted) setState(() {});
    }
  }

  void showNext() {
    if (!ttsPause) {
      ttsPause = true;
      FlutterForegroundTask.stopService();
    }
    ttsStart += 1;
    int i = ttsStart;
    if (mounted) {
      //_store.setInt('ttsStart', i);
      if (i == 0) {
        contentCtrl.text = spkList[0];
      } else {
        contentCtrl.text =
            spkList.sublist(i - 1, i + 1).join('\n==============>\n');
      }
      if (mounted) setState(() {});
    }
  }

  void showLast() {
    if (!ttsPause) {
      ttsPause = true;
      FlutterForegroundTask.stopService();
    }
    if (ttsStart > 0) {
      ttsStart -= 1;
    }

    int i = ttsStart;
    if (mounted) {
      //_store.setInt('ttsStart', i);
      if (i == 0) {
        contentCtrl.text = spkList[0];
      } else {
        contentCtrl.text =
            spkList.sublist(i - 1, i + 1).join('\n==============>\n');
      }
      if (mounted) setState(() {});
    }
  }

  bool loadingUrl = false;
  Future<void> playUrl(String value) async {
    ttsPause = false;
    //file path
    if (value.startsWith('file:')) {
      //startPlay();
      //errMsg = "play ${value.substring(5)}";
      await playFilePath(value.substring(5));
      return;
    }
    if (value.startsWith('clipboard')) {
      startPlay();
      return;
    }
    if (!value.startsWith('http')) {
      contentCtrl.text = '"$value" is not URL';
      return;
    }
    loadingUrl = true;
    //ttsStart = 0;
    //spkList = List.empty();
    contentCtrl.text = '';
    if (mounted) setState(() {});
    var val = await readStringFromUrl(value);
    String text1 = '';
    String html1 = val['body'].toString();
    switch (val['type'].toString()) {
      case 'text/plain':
        text1 = html1;
        links = List<List<String>>.empty();
      default:
        text1 = getTextFromHtml(html1, getCharset(val['type'].toString()));
        links = getLinksFromHtml(html1, value);
    }
    var list1 = splitText(text1);
    if (list1.isEmpty) return;
    spkList = mergeTextLimited(list1);

    storeContent().then((value) {
      startPlay();
      if (mounted) {
        setState(() {
          loadingUrl = false;
        });
      }
    });
  }

  Widget getAnimation() {
    if (loadingUrl) {
      return const CircularProgressIndicator();
    } else {
      return AnimationText(text: contentCtrl.text);
    }
  }

  Future<void> playFile() async {
    ttsPause = false;
    checkStoragePermission();
    const params = OpenFileDialogParams(
      dialogType: OpenFileDialogType.document,
    );
    final filePath = await FlutterFileDialog.pickFile(params: params);
    // FilePickerResult? result = await FilePicker.platform.pickFiles();
    // if (result == null) {
    //   return;
    // }
    File file1 = File(filePath!);

    var text1 = await file1.readAsString();
    var list1 = splitText(text1);
    if (list1.isEmpty) return;
    spkList = mergeTextLimited(list1);

    if (mounted) {
      setState(() {
        links = List<List<String>>.empty();
        urlCtrl.text = 'file:${filePath}';
        contentCtrl.text = text1;
        storeContent().then((value) {
          startPlay();
          if (mounted) setState(() {});
        });
      });
    }
  }

  Future<void> playFilePath(String p) async {
    ttsPause = false;
    File file1 = File(p);
    var text1 = await file1.readAsString();
    var list1 = splitText(text1);
    if (list1.isEmpty) {
      //errMsg = "Text is empty.";
      //setState(() {});
      return;
    }
    spkList = mergeTextLimited(list1);

    if (mounted) {
      setState(() {
        links = List<List<String>>.empty();
        urlCtrl.text = 'file:$p';
        contentCtrl.text = text1;
        storeContent().then((value) {
          startPlay();
          if (mounted) setState(() {});
        });
      });
    }
  }

  Future<void> playClipboardTxt() async {
    String text1 = await FlutterClipboard.paste();
    var list1 = splitText(text1);
    if (list1.isEmpty) return;
    String header =
        (list1[0].length > 20) ? list1[0].substring(0, 20) : list1[0];
    spkList = mergeTextLimited(list1);

    if (mounted) {
      setState(() {
        links = List<List<String>>.empty();
        urlCtrl.text = 'clipboard:$header';
        contentCtrl.text = text1;
        storeContent().then((value) {
          startPlay();
          if (mounted) setState(() {});
        });
      });
    }
  }

  Future<void> storeContent() async {
    await _store.setStringList('spkList', spkList);
    await _store.setString('url', urlCtrl.text);
    await _store.setDouble('speed', speed);
    await _store.setString('links', jsonEncode(links));
  }

  Future<void> restoreStatus() async {
    try {
      if (_store.getStringList('spkList') != null) {
        spkList = _store.getStringList('spkList')!;
      }
      if (_store.getInt('ttsStart') != null) {
        ttsStart = _store.getInt('ttsStart')!;
      }
      if (_store.getString('url') != null) {
        urlCtrl.text = _store.getString('url')!;
      }
      if (_store.getString('links') != null) {
        links = jsonDecode(_store.getString('links')!);
      }
      //print('fhz:start ${ttsStart}');

      startPlay(keep: true);
    } catch (e) {
      //print('fhz:' + e.toString());
      contentCtrl.text = 'error: $e';
      if (mounted) setState(() {});
    }
  }

  List<DropdownMenuItem> _buildLinks() {
    var ret = List<DropdownMenuItem>.empty(growable: true);
    ret.add(const DropdownMenuItem(
      value: '',
      child: Text('链接'),
    ));
    for (var e in links) {
      var item = DropdownMenuItem(
        value: e[1],
        child: SizedBox(
          width: 56,
          child: Text(
            e[0],
            softWrap: false,
            overflow: TextOverflow.visible,
          ),
        ),
      );
      ret.add(item);
    }
    return ret;
  }

  @override
  void dispose() {
    _store.setInt('ttsStart', ttsStart);
    super.dispose();
    WakelockPlus.disable();
  }

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    WidgetsFlutterBinding.ensureInitialized();
    _requestPermissionForAndroid().then((value) {
      _initForegroundTask();
    });

    ttsInit();
    flutterTts.getDefaultEngine.then((value) {
      if (mounted) {
        setState(() {
          engine = value.toString();
        });
      }
    });

    SharedPreferences.getInstance().then((value) {
      _store = value;
      if (_store.getDouble('speed') != null) {
        speed = _store.getDouble('speed')!;
      } else {
        _store.setDouble('speed', speed);
      }
    });
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    super.didChangeAppLifecycleState(state);
    // 当应用程序状态发生变化时，这个方法会被调用
    switch (state) {
      case AppLifecycleState.inactive:
        // 应用程序处于非活跃状态，但未进入后台
        break;
      case AppLifecycleState.resumed:
        // 应用程序处于前台
        break;
      case AppLifecycleState.paused:
        // 应用程序处于后台
        if (!bgRun) {
          handleAppPaused();
          bgRun = false;
        }
        FlutterForegroundTask.setOnLockScreenVisibility(true);
        break;
      case AppLifecycleState.detached:
        // 应用程序将要退出
        break;
      case AppLifecycleState.hidden:
        break;
    }
  }

  void handleAppPaused() {
    // 处理应用程序转到后台的逻辑
    setTtsPause();
    if (mounted) setState(() {});
  }

  void setTtsPause() {
    WakelockPlus.disable();
    if (!ttsPause) {
      ttsPause = true;
      FlutterForegroundTask.stopService();
    }
  }

  var linksCtrl = TextEditingController();
  var searchKey = TextEditingController();
  @override
  Widget build(BuildContext context) {
    // This method is rerun every time setState is called, for instance as done
    // by the _incrementCounter method above.
    //
    // The Flutter framework has been optimized to make rerunning build methods
    // fast, so that you can just rebuild anything that needs updating rather
    // than having to individually change instances of widgets.
    return WithForegroundTask(
        child: Scaffold(
      appBar: AppBar(
        // TRY THIS: Try changing the color here to a specific color (to
        // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
        // change color while the other colors stay the same.
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text(widget.title),
      ),
      body: Center(
        // Center is a layout widget. It takes a single child and positions it
        // in the middle of the parent.
        child: Column(
          // Column is also a layout widget. It takes a list of children and
          // arranges them vertically. By default, it sizes itself to fit its
          // children horizontally, and tries to be as tall as its parent.
          //
          // Column has various properties to control how it sizes itself and
          // how it positions its children. Here we use mainAxisAlignment to
          // center the children vertically; the main axis here is the vertical
          // axis because Columns are vertical (the cross axis would be
          // horizontal).
          //
          // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint"
          // action in the IDE, or press "p" in the console), to see the
          // wireframe for each widget.
          children: <Widget>[
            Text('TTS Engine: $engine'),
            Wrap(
              children: [
                IconButton(
                    onPressed: () {
                      if (!ttsPause) {
                        setTtsPause();
                      } else {
                        //ttsPause=false;
                        continuePlay();
                      }
                      if (mounted) setState(() {});
                    },
                    icon: (ttsPause)
                        ? const Icon(Icons.play_circle)
                        : const Icon(Icons.pause)),
                IconButton(
                    onPressed: () {
                      showNext();
                    },
                    icon: const Icon(Icons.skip_next)),
                IconButton(
                    onPressed: () {
                      showLast();
                    },
                    icon: const Icon(Icons.skip_previous)),
                TextField(
                  controller: searchKey,
                  onChanged: (val) {
                    if (val.isEmpty) return;
                    for (int i = 0; i < spkList.length; i++) {
                      if (spkList[i].contains(val)) {
                        showText(i);
                        break;
                      }
                    }
                  },
                  onTap: () {
                    searchKey.text = '';
                  },
                  decoration: const InputDecoration(hintText: '搜索定位'),
                ),
                IconButton(
                    onPressed: () {
                      playUrl(urlCtrl.text);
                    },
                    icon: const Icon(Icons.play_arrow)),
                IconButton(
                    onPressed: () {
                      playFile();
                    },
                    icon: const Icon(Icons.file_open)),
                ElevatedButton(
                    onPressed: () {
                      restoreStatus();
                    },
                    child: const Text('恢复进度')),
                ElevatedButton(
                    onPressed: () {
                      FlutterClipboard.paste().then((value) {
                        if (mounted) {
                          setState(() {
                            urlCtrl.text = value;
                          });
                        }
                      });
                    },
                    child: const Text('剪贴板URL')),
                DropdownButton(
                  value: '',
                  onChanged: (value) {
                    urlCtrl.text = value;
                    playUrl(value);
                  },
                  items: _buildLinks(),
                ),
                ElevatedButton(
                  onPressed: () {
                    playClipboardTxt();
                  },
                  child: const Text('剪贴板TXT'),
                ),
                ElevatedButton(
                  onPressed: () {
                    //转为后台运行
                    bgRun = true;
                    MoveToBackground.moveTaskToBack();
                  },
                  child: const Text('后台运行'),
                ),
              ],
            ),
            Text('Speed:${speed.toStringAsFixed(1)}'),
            Slider(
              value: speed,
              onChanged: (value) {
                //print('fhz: speed $speed');
                setTtsPause();
                if (mounted) {
                  setState(() {
                    speed = value;
                    _store.setDouble('speed', speed);
                  });
                }
              },
              min: 0.1,
              max: 1.7,
              divisions: 16,
              label: 'Speed:$speed',
            ),
            TextField(
              controller: urlCtrl,
              decoration: const InputDecoration(hintText: '网页URL'),
            ),
            getAnimation(),
            //Text(errMsg),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
          onPressed: () {
            var val = searchKey.text;
            if (val.isEmpty) return;
            for (int i = ttsStart + 1; i < spkList.length; i++) {
              if (spkList[i].contains(val)) {
                showText(i);
                break;
              }
            }
          },
          child: const Text('>')),
    ));
  }
}
